-- Copyright (c) 2015,2016 Lucky Byte, Inc.
--
-- Summary : POS 009.1 特殊的功能模块
--
-- Author  : 吴小虎 <xiaohu@lucky-byte.com>
-- Since   : 2016.4.29
--
-- History
--  * 2015.5.6 : 创建此文件
--

require "core.p091.trinfo"
require "core.p091.syscode"

-- 新建报文
--
lib091.new_packet = function()
    return lib091._new_packet()
end


-- 验证报文MAC
--
lib091.verify_mac = function(packet, tmk, mak, mac_algor)
    assert(packet._class == 'Packet091', string.format(
        'lib091.verify_mac() 需要 Packet091 报文，当前报文类型为[%s]', packet._class))

    if string.trimlen(tmk) == 0 then
        notify.error('终端主密钥为空，不能验证报文MAC.')
        return false
    end
    if string.trimlen(mak) == 0 then
        logger.error('MAC密钥为空，不能验证报文MAC，请重新签到.')
        return false
    end
    return lib091._verify_mac(packet, tmk, mak, mac_algor)
end


-- 添加报文MAC
--
lib091.set_mac = function(packet, tmk, mak, mac_algor)
    assert(packet._class == 'Packet091', string.format(
        'lib091.set_mac() 需要 Packet091 报文，当前报文类型为[%s]', packet._class))

    return lib091._set_mac(packet, tmk, mak, mac_algor)
end


-- 计算消费交易手续费
--
local _calc_merch_fee = function(merch, tramt, fee_name, dcflag)
    local _fee_uuid = merch.get(fee_name)
    if not _fee_uuid then
        logger.error('计算交易手续费错误[商户[%s][%s]未配置手续费[%s]].',
            merch.get('merno'), merch.get('name'), fee_name)
        return nil
    end
    return core.fee.calc({
        uuid  = _fee_uuid, tramt = tramt, debit = (dcflag == 1)
    })
end

-- 计算手续费
--
local _calc_fee = function(merch, packet, trinfo, dcflag)
    local _tramt = packet.get('4')
    if string.trimlen(_tramt) == 0 then
        logger.error('计算交易[%s]的手续费错误[报文中缺少金额字段].', trinfo.name)
        return nil
    end
    _tramt = tonumber(_tramt) / 100

    -- 消费交易手续费
    if trinfo.code == 't20' then
        return _calc_merch_fee(merch, _tramt, 't20_fee', dcflag)
    end
    logger.fatal('不能计算交易[%s]的手续费，请检查...', trinfo.name)
    return nil
end


-- 判定借贷记标志
local _get_dcflag = function(trinfo, respkt, reqpkt)
    if not trinfo.settl then
        logger.debug('非清算类交易无需判定卡借贷记标志.')
        return nil
    end
    local _pri_acct = respkt.get('2') or reqpkt.get('2')
    if string.trimlen(_pri_acct) == 0 then
        logger.info('判定借贷记卡错误[报文中无账号字段].')
        return nil
    end
    local _bininfo = core.cardbin.query(_pri_acct)
    if not _bininfo then
        return 99
    end
    return _bininfo.get('debit_credit_flag') == 1 and 1 or 2
end


-- 通过 POS 009.1 渠道转发请求报文
--
lib091.forward = function(args)
    assert(args.packet._class == 'Packet091', string.format(
        'lib091.forward() 需要 Packet091 报文，当前报文类型为[%s]', args.packet._class))

    if not args.merch then
        logger.warn('转发 POS 009.1 报文未提供商户信息.')
        return nil
    end

    -- 交易流水 UUID
    if not args.uuid then
        logger.warn('转发 071 报文时未提供交易流水 UUID.')
        args.uuid = uuid()
    end

    -- 如果没有提供交易信息，则查询交易信息
    if not args.trinfo then args.trinfo = lib091.trinfo(args.packet) end

    -- 重组模式下需要检查交易控制
    if args.indirect then
        if args.trinfo.trctl then
            local _tramt = args.packet.get('4')
            if string.trimlen(_tramt) > 0 then
                _tramt = tonumber(_tramt) / 100
            else
                _tramt = nil
            end
            local _rescode = core.trctl.check({
                merch = args.merch, trinfo = args.trinfo, tramt = _tramt
            })
            if _rescode ~= '00' then
                logger.error('商户[%s]配置的交易控制不允许继续转发当前的交易...',
                    args.merch and args.merch.get('merno') or '')
                return {
                    respkt  = lib091.respkt(args.packet, _rescode),
                    rescode = _rescode
                }
            end
            logger.debug('交易控制检测通过，继续转发此交易...')
        end
    end

    -- 记录转发交易流水
    local _trans_record = {
        uuid       = args.uuid,
        incoming   = false,
        chnl       = 1,
        indirect   = args.indirect,
        trinfo     = core.json.encode(args.trinfo),
        i_pkt      = args.packet.tojson(),
    }
    -- 登记流水中的商户信息
    _trans_record.merch = args.merch.tojson()

    -- 执行转发
    local _fowd = lib091._forward(args.packet, args.indirect)
    if _fowd then
        _trans_record.netinfo = _fowd.netinfo   -- 接出渠道网络通讯信息
        if _fowd.respkt then                    -- 接出渠道返回报文信息
            _trans_record.o_pkt = _fowd.respkt.tojson()
            _trans_record.status = 0
            local _code, _desc = lib091.rescode(_fowd.respkt)
            _trans_record.notes = _code and _code .. '：' .. _desc or '无响应码'

            _trans_record.dcflag = _get_dcflag(
                args.trinfo, _fowd.respkt, args.packet)

            -- 如果交易成功，则计算手续费
            if lib091.respkt_ok(_fowd.respkt) then
                if args.trinfo.hasfee then
                    _trans_record.fee = _calc_fee(args.merch,
                        _fowd.respkt, args.trinfo, _trans_record.dcflag)
                end
            end
        else
            _trans_record.status = 1
            _trans_record.notes = '未收到响应报文'
        end
    end
    if jdbc.upsert('pay_trlist', _trans_record) < 0 then
        logger.error(string.format('记录转发交易[%s]的流水失败.', args.uuid))
    end
    _fowd.trans_record = _trans_record
    return _fowd
end


-- POS 规范应答码表
local _pos_rescode_t = require "core.rscode.pos"

-- 创建响应报文
--
-- 参数 sys_rescode 为平台应答码
--
lib091.respkt = function(packet, sys_rescode)
    assert(packet._class == 'Packet091', string.format(
        'lib091.respkt() 需要 Packet091 报文，当前报文类型为[%s]', packet._class))

    -- 映射为 POS 标准错误码
    local _pos_rescode = lib091.sys_rescode_t[sys_rescode]
    if not _pos_rescode then
        logger.fatal('系统响应码[%s]不存在.', sys_rescode)
        _pos_rescode = '96'
    end

    local _respkt = lib091.new_packet()

    _respkt.tpdu(packet.tpdu())
    _respkt.head(packet.head())
    _respkt.dial_ani(packet.dial_ani())
    _respkt.dial_dni(packet.dial_dni())

    local _msg_type = tonumber(packet.get('1')) + 10
    _respkt.set('1',  string.pad(tostring(_msg_type), 4, false, '0'))
    _respkt.set('39', _pos_rescode)

    -- 如果是错误报文，则设置一些额外字段
    if sys_rescode ~= '00' then
        _respkt.set('12', os.date('%H%M%S'))
        _respkt.set('13', os.date('%m%d'))
        _respkt.set('37', os.date('%y%m%d%H%M%S'))
        _respkt.set('41', packet.get('41') or '00000000')
        _respkt.set('42', packet.get('42') or '000000000000000')
    end
    return _respkt
end


-- 获取报文中的应答码及对应的文字描述
--
lib091.rescode = function(packet)
    if not packet then return '' end

    assert(packet._class == 'Packet091', string.format(
        'lib091.rescode() 需要 Packet091 报文，当前报文类型为[%s]', packet._class))

    local  _pos_rescode = packet.get('39')
    return _pos_rescode, _pos_rescode_t[_pos_rescode] or '未知错误'
end


-- 判断报文应答码是否表示成功
--
lib091.respkt_ok = function(packet)
    assert(packet._class == 'Packet091', string.format(
        'lib091.respkt_ok() 需要 Packet091 报文，当前报文类型为[%s]', packet._class))
    return packet.get('39') == '00'
end
